package serialisation;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
import java.util.StringTokenizer;

import reservation.Billet;
import reservation.Client;
import reservation.Trajet;
import reservation.Transport;
import data.Classe;
import data.Condition;
import data.Couchette;
import data.Repas;
import data.TypePassager;
import data.TypeTransportHelper;
import data.VilleHelper;
import errors.NegativeValueException;
import errors.StartStopException;

/**
 * Classe SerializeText : Enregistrement de données dans un fichier texte
 * 
 * @author Alexandre Boursier and Mamadou Diallo and Cyril Gorrieri and Emmanuel
 *         Menage
 * @version 2012.01.23
 */

public class SerializeText extends Serializer {

	public SerializeText(String fichier) {
		super(fichier);
	}

	/**
	 * Utiliser le délimitateur pour la création d'objets
	 */
	private static final String separator = ";";
	
	/**
	 * Creér une chaîne de caractères à partir d'un tableau de chaînes et d'un
	 * separateur
	 * 
	 * @param att
	 *            La liste contenant les attributs cibles
	 * @param separator
	 *            Le séparateur cible
	 * @return la chaîne résultante
	 */
	public static String creerChaine(List<String> att) {
		// Controler la validité des paramètres
		if (att.size() == 0)
			throw new NullPointerException();

		// Créer la chaîne de caractères resultante en concatenant
		// les éléments de la liste
		StringBuilder sb = new StringBuilder();
		Iterator<String> iter = att.iterator();
		while (iter.hasNext()) {
			String s = iter.next();
			sb.append(s);
			sb.append(separator);
		}

		// Renvoyer le resultat
		return sb.toString();
	}

	/**
	 * Récupérer la ligne d'un fichier dans un tableau pour la reconnaissance
	 * d'attributs et mots-clés
	 * 
	 * @param buffer
	 *            La ligne cible
	 * @return un tableau d'attributs et de mots-clés d'une ligne d'un fichier
	 */
	public static String[] getChaine(String buffer) {
		// Decouper la ligne lue
		StringTokenizer st = new StringTokenizer(buffer, separator);

		// Stocker chaque champ de la ligne dans un tableau
		String[] token = new String[st.countTokens()];
		for (int i = 0; i < token.length; i++)
			token[i] = st.nextToken();

		return token;
	}

	/**
	 * Contrôler l'existence d'un fichier à partir de son chemin
	 * 
	 * @param path
	 *            Le chemin du fichier cible
	 */
	public boolean isExist() {
		// Obtenir une description du fichier a créer
		File f = new File(fichier);

		// Contrôler l'existence du fichier
		if (f.exists())
			return true;
		else
			return false;
	}
	
	private void initFichier() {
		try {
			File f = new File(fichier);
			f.delete();
			creerFichier();
		} catch (Exception e) {
			System.out.println("Impossible de réinitialiser le fichier !");
		}
	}

	public boolean store(List<Transport> transports, List<Trajet> trajets) {
		// On reinitialise le fichier
		initFichier();
		
		try {
			for (Transport t : transports) {
				if (store(t) == false)
					return false;
			}
			for (Trajet t : trajets) {
				if (store(t) == false)
					return false;
			}
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * Ecrire une chaîne de caractères à la fin d'un fichier
	 * 
	 * @param line
	 *            La chaîne à inclure dans le fichier
	 * @throws IOException
	 * @throws StartStopException
	 * @throws NegativeValueException
	 * @throws NumberFormatException
	 */
	public boolean store(String line) throws NullPointerException,
			IOException {
		// Contrôler la validité des paramètres
		if (line == null)
			throw new NullPointerException();

		// Contrôler l'existence du fichier
		if (!isExist())
			throw new FileNotFoundException();

		// Initialiser la lecture du fichier
		FileReader fr = null;
		try {
			fr = new FileReader(fichier);
		} catch (IOException e) {
			return false;
		}
		BufferedReader br = new BufferedReader(fr);

		// Initialiser l'ecriture dans le fichier
		FileWriter fw = null;
		try {
			fw = new FileWriter(fichier, true);
		} catch (IOException e) {
			return false;
		}
		BufferedWriter bw = new BufferedWriter(fw);

		// Parcourir l'ensemble du fichier
		String buffer = null;
		do {
			try {
				buffer = br.readLine();
			} catch (IOException e) {
				return false;
			}
		} while (buffer != null);

		// Ecrire la chaine passee en parametre dans le fichier
		// et effectuer un passage a la ligne
		try {
			bw.write(line + "\n");
		} catch (IOException e) {
			return false;
		}
		// Terminer la lecture et l'ecriture du fichier
		try {
			bw.close();
			fw.close();
			br.close();
			fr.close();
		} catch (IOException e) {
			return false;
		}
		return true;
	}

	/**
	 * Overloading store : Méthode store pour l'objet de type transport
	 * 
	 * @param t
	 *            Le transport à ajouter dans le fichier texte
	 */
	public boolean store(Transport t) throws NullPointerException,
			IOException {
		// Créer une chaîne qui sera la future dernière ligne du fichier
		if (store(SerializeText.creerChaine(t.parseText()))) {
			return true;
		}
		return false;
	}

	/**
	 * Overloading : Méthode store pour l'objet de type transport
	 * 
	 * @param t
	 *            Le trajet à ajouter dans le fichier texte
	 */
	public boolean store(Trajet t) throws NullPointerException,
			IOException {
		// Créer une chaîne qui sera la future dernière ligne du fichier
		if (store(SerializeText.creerChaine(t.parseText()))) {
			// Enregistrer tous les clients correspondants au trajet
			// initialement créé
			for (Billet b : t.getBillet()) {
				// Parser l'objet client
				List<String> attributs = b.parseText();
				attributs.add(String.valueOf(t.getIdentTrajet()));
				if (!store(SerializeText.creerChaine(attributs)))
					return false;
			}
			return true;
		}
		return false;
	}

	/**
	 * Charger la liste des transports et des trajets
	 * 
	 * @param separator
	 *            Le délimitateur cible
	 * @throws NegativeValueException
	 * @throws NumberFormatException
	 * @throws StartStopException
	 * @throws IOException
	 * @throws ParseException
	 */
	public boolean load(LinkedList<Transport> transports,
			LinkedList<Trajet> trajets) {
		transports.clear();
		trajets.clear();
		
		// Trier le fichier pour obenir un ordre logique de récupération
		// d'objets
		try {
			ecrireLignes(recupererLignesOrdonnees());
		} catch (Exception e1) {
			return false;
		}

		// Definir un scanner sur le fichier
		Scanner scan = null;
		try {
			scan = new Scanner(new File(fichier));
		} catch (FileNotFoundException e) {
			return false;
		}

		String buffer = null;

		// Parcourir l'ensemble du fichier
		while (scan.hasNextLine()) {

			// Lire la ligne courante du fichier
			buffer = scan.nextLine();

			String[] token = SerializeText.getChaine(buffer);

			if (token[0].equals("TRANSPORT")) {
				// Créer le transport associé
				Transport t;
				try {
					t = new Transport(Integer.parseInt(token[1]),
							TypeTransportHelper.getTypeTransport(token[2]), Integer
									.parseInt(token[3]));
				} catch (Exception e) {
					return false;
				}

				// Ajouter le transport à la liste
				transports.add(t);
			} else if (token[0].equals("TRAJET")) {
				Transport tCible = null;
				// Trouver le transport associé au trajet
				for (Transport t : transports) {
					if (t.getIdentTransport() == Integer.parseInt(token[5]))
						tCible = t;
				}

				// Créer le trajet associé
				Trajet t;
				try {
					t = new Trajet(Integer.parseInt(token[1]), VilleHelper
							.getVille(token[2]), VilleHelper.getVille(token[3]),
							Trajet.toFormatedDate(token[4]), tCible);
				} catch (Exception e) {
					return false;
				}

				// Ajouter le transport à la liste
				trajets.add(t);
			} else if (token[0].equals("BILLET")) {
				for (Trajet t : trajets) {
					if (t.getIdentTrajet() == Integer.parseInt(token[1])) {
						t.addBillet(new Billet(t, new Client(token[2]),
								token[3], Repas.valueOf(token[4]), Boolean
										.valueOf(token[5]), TypePassager
										.valueOf(token[6]), Classe
										.valueOf(token[7]), Condition
										.valueOf(token[8]), Couchette
										.valueOf(token[9])));
					}
				}
			}
		}
		Transport.setIdent(transports.getLast().getIdentTransport()+1);
		Trajet.setIdent(trajets.getLast().getIdentTrajet()+1);
		return true;
	}

	/**
	 * Récupérer les lignes du fichier texte cible
	 * 
	 * @return la liste des lignes du fichier de manière suivante
	 *         (TRANSPORTS-TRAJETS-PARTICIPANTS)
	 */
	public List<String> recupererLignesOrdonnees() {
		// Créer trois liste pour définir l'ordre de transcription
		List<String> listeTransport = new ArrayList<String>();
		List<String> listeTrajet = new ArrayList<String>();
		List<String> listeBillet = new ArrayList<String>();

		// Definir un scanner sur le fichier
		Scanner scan = null;
		try {
			scan = new Scanner(new File(fichier));
		} catch (FileNotFoundException e) {
		}

		// Initialiser un buffer pour contenir les lignes du fichier
		String buffer = null;

		// Parcourir l'ensemble du fichier
		while (scan.hasNextLine()) {
			// Lire la ligne courante du fichier
			buffer = scan.nextLine();
			String[] token = SerializeText.getChaine(buffer);

			// Ajouter les transports
			if (token[0].equals("TRANSPORT")) {
				listeTransport.add(buffer);
			}
			// Ajouter les trajets
			else if (token[0].equals("TRAJET")) {
				listeTrajet.add(buffer);
			}
			// Ajouter les voyageurs
			else if (token[0].equals("BILLET")) {
				listeBillet.add(buffer);
			}
		}
		// Constuire la liste des lignes courantes par ordre prédéfini
		List<String> resultat = new ArrayList<String>();
		for (String s : listeTransport) {
			resultat.add(s);
		}
		for (String s : listeTrajet) {
			resultat.add(s);
		}
		for (String s : listeBillet) {
			resultat.add(s);
		}
		if (resultat.size() == 0)
			System.out
					.println("Il n'y a aucune donnée enregistrée dans le fichier");
		return resultat;
	}

	/**
	 * Trier le fichier texte de la façon suivante :
	 * 
	 * Transports Trajets Voyageurs
	 * 
	 * @throws IOException
	 * @throws NullPointerException
	 * @throws StartStopException
	 * @throws NegativeValueException
	 * @throws NumberFormatException
	 * 
	 */
	public void ecrireLignes(List<String> ligne)
			throws NullPointerException, IOException, NumberFormatException,
			NegativeValueException, StartStopException {
		// Ecraser le fichier courant
		initFichier();

		// Ajouter dans un premier temps les transports, puis les trajets puis
		// les voyageurs
		for (String tri : ligne) {
			store(tri);
		}
	}
	
	@Override
	public String getExtention() {
		return "txt";
	}
}
